home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1990: Night of the Living Disc / Night of the Living Disc.hdv / Dev.CD.5 / Tools / Technical.Notes / IIGS / TN.IIGS.054 < prev    next >
Encoding:
Text File  |  1989-04-03  |  40.8 KB  |  1,111 lines  |  [04] ASCII Text (0x0000)

  1. Apple II
  2. Technical Notes
  3. _____________________________________________________________________________
  4.                                                   Developer Technical Support
  5.  
  6.  
  7. Apple IIGS
  8. #54:    MIDI Drivers
  9.  
  10. Written by:    Jim Mensch                                            May 1989
  11.  
  12. This Technical Note describes how to write a driver for use with the Apple 
  13. IIGS MIDI tools.
  14. _____________________________________________________________________________
  15.  
  16. Apple ships two drivers with the MIDI tool set, APPLE.MIDI and CARD6850.MIDI, 
  17. respectively.  These drivers are adequate for almost all MIDI hardware 
  18. currently on the market for the Apple IIGS; however, if your hardware is not 
  19. compatible with either of these drivers, you will have to write your own.  
  20. This Note includes all the information you need to create a MIDI driver.
  21.  
  22.  
  23. Purpose of the Driver and Description of Hardware Requirements
  24.  
  25. The Apple MIDI tools communicate to the MIDI world via a simple driver.  The 
  26. driver's function is managing the transmission and reception of single bytes 
  27. of MIDI data between the tools and the particular MIDI hardware involved.  The 
  28. MIDI tools operate on the assumption that the hardware has a method of 
  29. interrupting the system when a character has been received and when a 
  30. character can be transmitted.  Since there is quite a bit of overhead in 
  31. processing MIDI data, and since MIDI data can comes across a standard MIDI bus 
  32. at a rate of over 3000 bytes per second, it is suggested that you provide a 
  33. means for your device to buffer a few characters to reduce system overhead 
  34. caused by interrupts if you are designing hardware to be used with the MIDI 
  35. tools.
  36.  
  37.  
  38. Format of the Driver File
  39.  
  40. The driver file is a standard OMF load file, which can be created with any of 
  41. the popular Apple IIGS assemblers.  The file must start with a dispatch table 
  42. that contains the addresses of the standard driver routines.  All driver 
  43. routines must be in the same segment as the dispatch table.  The dispatch 
  44. table should have 13 four-byte entries, each of which contains the address of 
  45. the appropriate routine minus one.  Table 1 contains addresses of routines in 
  46. the MIDI driver to perform specific functions.
  47.  
  48.  
  49.               Call          Function
  50.               _____________________________________________________
  51.               Init          Called to initialize the port and prime
  52.                             the driver
  53.               ShutDown      Called to close the port and clean up 
  54.                             after driver
  55.               Reset         Called at reset time by the MIDI tools
  56.               IntHandler    Called when your interrupt occurs
  57.               PollRecv      Poll input the port for data
  58.               RecvIntOn     Turns on receiver interrupts
  59.               RecvIntOff    Turns off receiver interrupts
  60.               PollXmit      Polls the transmitter to see if another 
  61.                             character can be sent
  62.               XmitIntOn     Enables transmitter interrupts
  63.               XmitIntOff    Disables transmitter interrupts
  64.               NotImp        Currently unused
  65.               NotImp        Currently unused
  66.               NotImp        Currently unused
  67.               _____________________________________________________
  68.  
  69.                     Table 1-MIDI Driver Function Routines
  70.  
  71.  
  72. Routine Calling Conventions
  73.  
  74. All driver routines are called with full 16-bit mode enabled and should exit 
  75. the same way.  On entry to each routine, the accumulator contains the direct 
  76. page pointer that the driver should use if it wants to use the MIDI tools' 
  77. direct page.  It is the driver's responsibility to set the direct page 
  78. register and restore it on exit.  All other parameters are passed on the stack 
  79. and should be removed from the stack before the routine exits.  The MIDI tools 
  80. set aside 128 bytes of space on the passed direct page for use by the driver.  
  81. They are bytes $80–$FF.
  82.  
  83. If you want to report an error inside of any routine (except IntHandler), set 
  84. the carry flag on exit and load the accumulator with the error code.  Use 
  85. predefined error codes whenever possible.  If you need to report a device 
  86. specific error, use errors in the range $C0–$FF.  The MIDI tools will set the 
  87. high byte of the error code properly for you, so you do not need to do it 
  88. yourself.  Table 2 lists all of the potential predefined error codes.
  89.  
  90.       Error Code                Error Definition
  91.       _____________________________________________________________
  92.       miToolsErr ($2004)        The required tools were not started
  93.       miNoBufErr ($2007)        No buffer is currently allocated
  94.       miDevNotAvail ($2080)     Requested device is not 
  95.                                 available
  96.       miDevSlotBusy ($2081)     Requested slot is already in use
  97.       miDevBusy ($2082)         Requested device is already in use
  98.       miDevOverrun ($2083)      Device overrun by incoming MIDI 
  99.                                 data
  100.       miDevNoConnect ($2084)    No connection to MIDI
  101.       miDevReadErr ($2085)      Framing error in received MIDI 
  102.                                 data
  103.       miDevVersion ($2086)      ROM version is incompatible with 
  104.                                 driver
  105.       miDevIntHndlr ($2087)     Conflicting interrupt handler 
  106.                                 installed
  107.       _____________________________________________________________
  108.  
  109.                     Table 2-Predefined Error Codes
  110.  
  111.  
  112. The Driver Routines
  113.  
  114. Init
  115.  
  116. This routine is called by the MIDI tools when it wants to initialize your port 
  117. and tell the driver to prepare itself for the rest of the calls.  Figure 1 
  118. shows how the stack looks on entry to this call.
  119.  
  120.         | previous contents |
  121.         |-------------------|
  122.         |    long space     |  LONG - Space for result
  123.         |                   |
  124.         |-------------------|
  125.         |    NewIntAddr     |  LONG - Pointer to MIDI tools int vector
  126.         |                   |
  127.         |-------------------|
  128.         |     SlotNum       |  WORD - Number of slot/port to use
  129.         |-------------------|
  130.         |     SlotFlag      |  WORD - 0=Internal port 1=card in slot
  131.         |-------------------|
  132.         |      UserID       |  WORD - User ID of current application
  133.         |-------------------|
  134.         |   RTL   |   RTL   |
  135.         |-------------------|
  136.         |   RTL   |
  137.         |---------|  <------ Stack Pointer
  138.                                                     
  139.                     Figure 1–The Stack on Entry to Init
  140.  
  141. The Init routine should first test to see if the port specified by SlotFlag 
  142. and SlotNum is available for use.  SlotNum is the number of the slot or the 
  143. port that the user has requested for use, and SlotFlag indicates whether it is 
  144. a built-in port or a card in a slot.  After determining that the requested 
  145. device is available, you should initialize the device, allocate any memory 
  146. that your driver may require (beyond what is available in the direct page), 
  147. and set the proper system interrupt vector to the address passed in 
  148. NewIntAddr.  Before setting the vector, be sure to save the old value, as the 
  149. MIDI tools expect the result from this routine to be the old address stored in 
  150. the vector.  On exit, the stack should contain the return address and the old 
  151. vector address.
  152.  
  153. ShutDown
  154.  
  155. This routine is called when the MIDI tools want your driver to release the 
  156. MIDI device and prepare to be unloaded.  Figure 2 shows how the stack looks on 
  157. entry to this call.
  158.  
  159.         | previous contents |
  160.         |-------------------|
  161.         |   OldIntVector    |  LONG - Old interrupt vector pointer
  162.         |                   |
  163.         |-------------------|
  164.         |   RTL   |   RTL   |
  165.         |-------------------|
  166.         |   RTL   |
  167.         |---------|  <------ Stack Pointer
  168.                                                     
  169.                     Figure 2–The Stack on Entry to ShutDown
  170.  
  171. Your routine should change the interrupt vector that you used to OldIntVector.  
  172. It should then deallocate all the memory that it allocated, disable all 
  173. interrupts on the device, and if needed, tell the system that you are no 
  174. longer using the port in question.
  175.  
  176. Reset
  177.  
  178. This routine is called when the system has been reset by the user.  Figure 3 
  179. shows how the stack looks on entry to this call.
  180.  
  181.         | previous contents |
  182.         |-------------------|
  183.         |   OldIntVector    |  LONG - Old interrupt vector pointer
  184.         |                   |
  185.         |-------------------|
  186.         |   RTL   |   RTL   |
  187.         |-------------------|
  188.         |   RTL   |
  189.         |---------|  <------ Stack Pointer
  190.                                                     
  191.                     Figure 3–The Stack on Entry to Reset
  192.  
  193. All you should do at this point is attempt to deallocate any memory you were 
  194. using and disable interrupts on the device you were using.
  195.  
  196. Note:    Do not set the interrupt vector to OldIntVector, instead 
  197.          remove the value from the stack and dispose of it.
  198.  
  199. IntHandler
  200.  
  201. The IntHandler routine is called by the MIDI tools when an interrupt occurs 
  202. for the vector that you are using.  The MIDI driver performs some setup then 
  203. calls your routine.  This routine does not have any parameters on the stack.
  204.  
  205. Once called, your IntHandler routine should test the port to see if an 
  206. interrupt has occurred on your device.  If your device did not cause the 
  207. interrupt, you should set the carry and exit as quickly as possible, reducing 
  208. the system interrupt overhead.
  209.  
  210. If your device caused the interrupt, you should test the receiver to see if 
  211. any bytes of data are waiting to be read.  If there is data waiting, you 
  212. should load that data into the accumulator and perform a JSL to the following 
  213. code:
  214.  
  215.     InBufGlue    PEA $0400
  216.                  PHD
  217.                  RTL
  218.  
  219. This code calls the MIDI tools and tell them to accept the character in the 
  220. accumulator into its input buffer.  After accepting the data, control is 
  221. passed back to the instruction following your JSL.  If you received a byte of 
  222. data and an error occurred during reception, you should load the number of the 
  223. error code into the y register and perform a JSL to the following code:
  224.  
  225.     InErrGlue    PEA $0500
  226.                  PHD
  227.                  RTL
  228.  
  229. Again, you will regain control right after the JSL.  Once in your interrupt 
  230. routine, you may perform the calls above for as much data as you like.  For 
  231. example, if your device has a three-byte buffer, you could call InBufGlue once 
  232. for each waiting character, thus reducing your interrupt overhead and possibly 
  233. preventing unneeded interrupts.
  234.  
  235.  
  236. If the transmitter on your device is ready to send data, you should perform a 
  237. JSL to the following code:
  238.  
  239.     OutBufGlue    PEA $8400
  240.                   PHD
  241.                   RTL
  242.  
  243. This routine will return with the carry set if no data is waiting to be 
  244. transmitted or the carry clear if data is available.  If data is waiting, the 
  245. next character to send will be in the accumulator, and you should simply send 
  246. it at that time.  If no more data is available, you should disable transmitter 
  247. interrupts and exit.  The MIDI tools will re-enable transmitter interrupts the 
  248. next time it has data to send.
  249.  
  250. PollRecv
  251.  
  252. The PollRecv (Poll Receive) routine is called by the MIDI tools every now and 
  253. then to see if any data might be waiting to be read.  There are no parameters 
  254. on the stack for this call.  Your driver should test to see if any data is 
  255. available and transmit it all to the MIDI tools via the InBufGlue described in 
  256. the IntHandler description.
  257.  
  258. PollXmit
  259.  
  260. The PollXmit (Poll Transmit) routine is called by the MIDI tools when any data 
  261. is added to the MIDI output buffer.  There are no parameters on the stack for 
  262. this routine.  Your driver should enable transmitter interrupts, test to see 
  263. if it can send any data immediately, and if it can, call OutBufGlue as 
  264. described int the IntHandler description to get data to send.
  265.  
  266. XmitIntOn and RecvIntOn
  267.  
  268. These routines are called when the MIDI tools want to explicitly enable 
  269. transmitter or receiver interrupts. They have no parameters on the stack and 
  270. should, when called, enable transmitter interrupts for XmitIntOn and receiver 
  271. interrupts for RecvIntOn.
  272.  
  273.  
  274. XmitIntOff and RecvIntOff
  275.  
  276. These routine are called when the MIDI tools want to explicitly disable 
  277. transmitter or receiver interrupts.  They have no parameters on the stack and 
  278. should, when called, disable transmitter interrupts for XmitIntOff and 
  279. receiver interrupts for RecvIntOff.
  280.  
  281. NotImp
  282.  
  283. These routines are not yet implemented, but your driver should be ready to 
  284. handle a call to them.  When called, they should clear the accumulator, clear 
  285. the carry and perform an RTL back to the MIDI tools.
  286.  
  287.  
  288.  
  289. A MIDI Driver Skeleton
  290.  
  291. You can use the following sample code as a basis for a MIDI driver.  It is not 
  292. a complete driver in itself, and you will need to add code where comments with 
  293. asterisks (***) appear for it to be functional.  This example is in MPW IIGS 
  294. assembler format.
  295.  
  296.  
  297. ******************************************************************************
  298. *
  299. * MIDI.DRVR.Aii
  300. *
  301. * (C)  Copyright Apple Computer, Inc. 1988
  302. * All rights reserved.
  303. *
  304. * by Don Marsh & Jim Mensch
  305. * 10/26/88
  306. *
  307. * This is a shell that can be used to create custom MIDI drivers for use with
  308. * the Apple MIDI tool set. This shell is not functional, but can be used as a
  309. * starting point for creating your own custom MIDI drivers.
  310. *
  311. * Files:    System Macros and equates
  312. *
  313. *
  314. *
  315. * Modification History:
  316. *
  317. * Version 1.0   Mensch
  318. *
  319. *      10/26/88
  320. *
  321. *      Create first    draft
  322. *
  323. ******************************************************************************
  324. *
  325.     Include 'E16.MIDI'
  326.     Include 'M16.MiscTool'
  327.     Include 'E16.MiscTool'
  328.     Include 'M16.util'
  329.  
  330. ;
  331. ; Direct page usage    Note:
  332. ; MIDI drivers may use the upper half ($80-$FF) of the MIDI direct page. When
  333. ; a MIDI driver routine is called the Accumulator will contain the direct page
  334. ; pointer for the MIDI tool set. If your driver requires more storage than 
  335. ; 128 bytes, it will have to allocate them itself using the memory manager.
  336.  
  337. theuserID     equ $80               ; location to store the passed user ID
  338. PortInUse     equ theuserID+2       ; storage for the port number in use
  339. deref         equ PortInUse+2
  340. Temp          equ Deref+4
  341.               EJECT
  342.  
  343. ******************************************************************************
  344. *
  345. *
  346. DispatchTable    RECORD
  347. *
  348. * Description:    Every MIDI Driver must start with a driver dispatch table 
  349. *      that contains the entry point minus 1 of each of the 
  350. *      required entry points.
  351. *
  352. *
  353. * Inputs:     None
  354. *
  355. * Outputs:    None
  356. *
  357. * External Refs:
  358.         Import DRVRInit
  359.         Import DRVRShutDown
  360.         Import DRVRReset
  361.         Import DRVRIntHandler
  362.         Import DRVRPollRecv
  363.         Import DRVRRecvIntOn
  364.         Import DRVRRecvIntOff
  365.         Import DRVRPollXmit
  366.         Import DRVRXmitIntOn
  367.         Import DRVRXmitIntOff
  368.         Import DRVRNotImplemented
  369. *
  370. * Entry Points:    None
  371. *
  372. ******************************************************************************
  373. *
  374.  
  375.               DC.L DRVRInit
  376.               DC.L DRVRShutDown
  377.               DC.L DRVRReset
  378.               DC.L DRVRIntHandler
  379.               DC.L DRVRPollRecv
  380.               DC.L DRVRRecvIntOn
  381.               DC.L DRVRRecvIntOff
  382.               DC.L DRVRPollXmit
  383.               DC.L DRVRXmitIntOn
  384.               DC.L DRVRXmitIntOff
  385.               DC.L DRVRNotImplemented
  386.               DC.L DRVRNotImplemented
  387.               DC.L DRVRNotImplemented
  388.  
  389.  
  390. ; a few of the routines will need a temporary storage location that can be
  391. ; used even after the direct page is set back to what it was, This is a good 
  392. ; place to put it!
  393.  
  394. ErrorCode     ds.W 1                     ; temporary holder of an error code
  395.               EndR
  396.     
  397.               EJECT
  398.  
  399. ******************************************************************************
  400. *
  401. *
  402. DRVRInit    PROC
  403. *
  404. * Description:    This is called by the MIDI Tools when it needs to Init
  405. *                 your MIDI Driver. This is usually in response to a MIDIxxx
  406. *                 call made by the application.
  407. *                 When this routine is called, you should allocate any buffer
  408. *                 space that you will need beyond the direct page, you should
  409. *                 enable the interrupts on your MIDI Device, and then set the
  410. *                 appropriate system interrupt vector and return the old vector
  411. *                 value. If the init works fine, clear the carry and return.
  412. *                 If an error occurs return the appropriate error code 
  413. *                 in the Accumulator, and set the carry.
  414. *
  415. *
  416. * Inputs:    UserID:Word          ID of application, for mem allocation
  417. *            SlotFlag:Word        0 for internal port/ 1 for slot
  418. *            SlotNum:Word         number of slot/port to use
  419. *            NewIntVector:Long    address to give system as its new
  420. *                                 interrupt vector. This routine is in the
  421. *                                 MIDI tool set, and it performs needed
  422. *                                 setup before it calls your interrupt 
  423. *                                 routine
  424. *
  425. * Outputs:   OldIntVector:Long    Address interrupt vector used to have
  426. *
  427. * External Refs:    None
  428. *
  429. * Entry Points:     None
  430. *
  431. ******************************************************************************
  432. *
  433. ; Offsets for parameters on the stack
  434.  
  435. ProcStatus     equ 1
  436. OldDPage       equ ProcStatus+1
  437. ReturnAddress  equ OldDPage+2
  438. UserID         equ ReturnAddress+3
  439. SlotFlag       equ UserID+2
  440. SlotNum        equ SlotFlag+2
  441. NewIntVector   equ SlotNum+2
  442. OldIntVector   equ NewIntVector+4
  443. ParmBytes      equ 10
  444. ParmEnd        equ ReturnAddress+ParmBytes
  445.  
  446. ; first disable interrupts since we are going to be setting up interrupt
  447. ; vectors and enabling interrupt generating hardware. We wouldn't want an
  448. ; interrupt to go off before we were ready to handle it! Then set us up to
  449. ; use the MIDI direct page.
  450.  
  451.                php                  ; save the old proc status
  452.                phd                  ; save the old direct page
  453.                tcd                  ; Set Direct page to the one passed
  454.                SEI                  ; and disable interrupts
  455.     
  456. ; now get the user ID and save it, and allocate any buffers that we may need
  457. ; Since most drivers will never need more than 128 bytes of storage we will
  458. ; not allocate any storage space
  459.  
  460.                lda UserID,s         ; first save the user ID for later
  461.                sta theUserID        ; in our section of the MIDI DPage
  462.     
  463. ; *** Insert any memory allocation needed here ***
  464.  
  465. ; Next, you should check the slot flag and number to see if they are 
  466. ; compatible with this driver. If they are, you should continue and
  467. ; initialize the proper port. If they are not proper, you should exit with
  468. ; an error.  For this example, I will be testing the SlotFlag, to see if
  469. ; it is set to external.
  470.  
  471.                lda SlotFlag,s       ; first test the slot flag to be sure
  472.                bne FlagOK           ; its non-zero.
  473.     
  474.                ldy #miDevNotAvail   ; if its zero, signal not available
  475.                bra InitError        ; and exit via error routine
  476.     
  477. FlagOK         lda SlotNum,s        ; Now save the slot number in 
  478.                sta PortInUse        ; our data area
  479.     
  480. ; *** At this point you should test the firmware in the desired slot to be 
  481. ; sure that the card you want is properly installed, if it is not then you
  482. ; should pass back the appropriate error ***
  483.  
  484. ; Now that you know that you have the proper slot information and you have 
  485. ; tested to be sure that you have the hardware needed for the driver it is
  486. ; time for you to initialize the interface and to enable its interrupts.
  487.  
  488. ; *** Install code to initialize your hardware/interrupts here ***
  489.  
  490. ; Now that the Port has been properly initialized, you must set up the proper
  491. ; system interrupt vector. Since we required an external card above it would
  492. ; make sense that you need to use the "Other unspecified interrupt handler"
  493. ; vector (Number $0017). But first, remember to get the original vector 
  494. ; pointer because we must return it to the MIDI tools.
  495.  
  496.                PushLong #0          ; space for result
  497.                PushWord #otherIntHnd  ; vector to retrieve
  498.                _GetVector           ; and get the vector in question
  499.                PullLong Temp        ; place in storage for a sec
  500.     
  501.                lda Temp             ; now place it on the stack
  502.                sta OldIntVector     ; as the result of this function
  503.                lda Temp+2
  504.                sta OldIntVector+2
  505.         
  506.                lda NewIntVector     ; now move the MIDI Interrupt routine
  507.                sta Temp             ; pointer into temporary storage
  508.                lda NewIntVector+2
  509.                sta Temp+2    
  510.         
  511.                PushWord #otherIntHnd ; now set the vector to point to
  512.                PushLong Temp        ; the MIDI drivers interrupt routine
  513.                _SetVector
  514.     
  515. ; The driver is now all set up, pull off the passed parms and we are done!
  516. Done           ldy #0               ; set the error code to 0. No error
  517. ;
  518. ; This is the alternate label for the Done routine that should be called when
  519. ; an error has occurred.
  520. InitError
  521.                lda ReturnAddress,s  ; Move the return address below the
  522.                sta ParmEnd,s        ; parameters
  523.                lda ReturnAddress+1,s
  524.                sta ParmEnd+1,s
  525.         
  526.                pld                  ; get the direct page back
  527.                plp                  ; get the processor status back
  528.  
  529.                tsc                  ; now adjust the stack pointer
  530.                sec                  ; so that the parameters are gone
  531.                sbc #ParmBytes
  532.                tcs                  ; now the return address is on Top
  533.         
  534.                tya                  ; put any error into <A>
  535.                cmp #1               ; set the carry if non-zero
  536.                RTL                  ; and return
  537.         
  538.                EndP
  539.         
  540.                EJECT
  541. ******************************************************************************
  542. *
  543. *
  544. DRVRShutDown    PROC
  545. *
  546. * Description:    This routine will be called whenever the MIDI Tools want
  547. *                 to cause your driver to let go of the port it was using.
  548. *
  549. *
  550. * Inputs:    OldIntVector:Long    Address to place back into the system 
  551. *                                 interrupt vector you were using
  552. *
  553. * Outputs:    Carry clear if successful
  554. *             Carry set if not, error in <A>
  555. *
  556. * External Refs:
  557.     Import DrvrRecvIntOff
  558.     Import DRVRXMitIntOff
  559. *
  560. * Entry Points:
  561. *
  562. ******************************************************************************
  563. *
  564.         With DispatchTable
  565.  
  566. ProcStatus     equ 1
  567. OldDPage       equ ProcStatus+1
  568. ReturnAddress  equ OldDPage+2
  569. OldIntVector   equ ReturnAddress+3
  570. ParmBytes      equ 4
  571. ParmEnd        equ ReturnAddress+ParmBytes
  572.  
  573. ; first disable interrupts since we are going to be setting up interrupt 
  574. ; vectors  We wouldn't want an interrupt to go off before we were ready
  575. ; to handle it!  Then set us up to use the MIDI direct page.
  576.  
  577.                php                  ; save the old proc status
  578.                phd                  ; save the old direct page
  579.                tcd                  ; Set Direct page to the one passed
  580.                SEI                  ; and disable interrupts
  581.         
  582.                lda #0               ; zero out the temp error code
  583.                sta >ErrorCode
  584. ; Now First, re-install the old interrupt vector
  585.  
  586.                lda OldIntVector     ; get the old vector off the stack
  587.                sta Temp             ; and save it in globals for a sec
  588.                lda OldIntVector+2
  589.                sta Temp+2
  590.         
  591.                PushWord #otherIntHnd ; now set the vector to point to
  592.                PushLong Temp        ; its original routine.
  593.                _SetVector
  594.  
  595. ; Next, turn off the interface hardware, and tell it to stop generating
  596. ; interrupts. We can share some code here and call our DRVRRecvIntOff and
  597. ; DRVRXmitIntOff routines. Always remember load the direct page into the
  598. ; accumulator.
  599.  
  600.                tdc                  ; get direct page into <A>
  601.                jsl DRVRXmitIntOff   ; and turn off transmitter interrupts
  602.     
  603.                tdc
  604.                jsl DRVRRecvIntOff   ; and now receiver interrupts.
  605.  
  606. ; *** Usually turning off interrupts will be all that you would need to do at
  607. ; this point, however, if your interface card requires extra shutdown code
  608. ; this is where you would place it ***
  609.  
  610. ; *** If you allocated any memory in the DRVRInit call, this is the place to 
  611. ; get rid of it.
  612.  
  613. ; If an error were to occur in this routine, you should simply store the error 
  614. ; number in our temporary error code variable like this
  615. ;
  616. ;               lda #ErrorNumber
  617. ;               Sta >ErrorCode
  618.  
  619.  
  620. Done
  621. ; Now that we are done shutting down the driver, pull off the passed data
  622. ; and end.
  623.                pld                  ; first retrieve the old dpage
  624.                plp                  ; and processor status
  625.         
  626.                Longa Off            ; next move the return address
  627.                SEP #$20             ; we need a short acc for this trick
  628.         
  629.                pla                  ; pull the 3 byte return address
  630.                ply                  ; into <A> and <Y>
  631.  
  632.                plx                  ; now remove the remaining bytes
  633.                plx                  ; of passed parameters
  634.         
  635.                phy                  ; and restore the return address
  636.                pha
  637.         
  638.                Longa On
  639.                REP #$30             ; and turn back on full 16-bit mode
  640.     
  641.                lda >ErrorCode       ; retrieve the error code
  642.                cmp #1               ; and set the carry if non-zero
  643.                RTL
  644.                EndP
  645.         
  646.                EJECT
  647.  
  648. ******************************************************************************
  649. *
  650. *
  651. DRVRReset    PROC
  652. *
  653. * Description:    This routine will be called whenever MIDIReset is called.
  654. *                 and that should only happen when an actual reset occurred.
  655. *                 It should in most cases perform the exact same functions
  656. *                 as MIDI Shutdown.
  657. *
  658. *
  659. * Inputs:    OldIntVector:Long    Original contents of interrupt vector
  660. *
  661. * Outputs:    None
  662. *
  663. * External Refs:
  664. *
  665. * Entry Points:
  666. *
  667. ******************************************************************************
  668. *
  669.  
  670.                jmp DRVRShutDown
  671.     
  672.                EndP
  673.     
  674.                EJECT
  675. ******************************************************************************
  676. *
  677. *
  678. DRVRIntHandler    PROC
  679. *
  680. * Description:    This routine is the very core of the MIDI driver. It takes
  681. *                 care of passing data back and forth between the MIDI tools
  682. *                 and your hardware. It will be called for both input and
  683. *                 output.
  684. *
  685. *
  686. * Inputs:     None
  687. *
  688. * Outputs:    Carry set if interrupt not serviced
  689. *
  690. * External Refs:    
  691.         Import DRVRXmitIntOff
  692. *
  693. * Entry Points:    
  694.         Export InBufGlue
  695.         Export InErrGlue
  696.         Export OutBufGlue
  697. *
  698. ******************************************************************************
  699. *
  700.     
  701.                phd                  ; first, save the current dpage
  702.                tcd                  ; and use the MIDI DPage
  703.         
  704. ; The first thing the interrupt routine should do is to test to see if the
  705. ; interrupt was actually generated by our port. If it was then we should 
  706. ; handle it, but if not, we should simply exit this routine with the carry
  707. ; set as fast as we can, so that the next interrupt handler will get it
  708. ; in a timely manner.
  709.  
  710. ; *** Insert code here to test to see if the original interrupt was yours ***
  711.  
  712.                beq ServicePort      ; if it was our, handle it
  713.     
  714. ; If the interrupt was not ours, set the carry and leave
  715.                pld                  ; restore the direct page
  716.                sec
  717.                rtl
  718.  
  719. ServicePort                         ; the interrupt was ours, continue
  720.  
  721. ; This routine should test the interrupt again, too see if the port is ready
  722. ; to transmit or receive, If it is ready to transmit or receive, it should 
  723. ; then call the ServiceRecv, or ServiceXMit routines
  724.  
  725. ; *** Insert code here to test for receive
  726.  
  727.                bne ServiceRecv      ; if chars waiting try receive it 
  728.     
  729. ; If no more characters are waiting, see if we are ready to transmit any 
  730. ; characters.
  731.  
  732.                bne ServiceXMit      ; if can send a character do it
  733.     
  734. ; If both the above tests fail, then exit the interrupt handler for now
  735.                pld                  ; restore the direct page
  736.                clc                  ; clear the carry to indicate serviced
  737.                RTL                  ; and return
  738.     
  739. ; The following routine ServiceRecv will be called when a character is waiting
  740. ; It should retrieve that character, pass it to the MIDI drivers, and then
  741. ; branch back to the beginning of ServicePort, to see if any more chars are
  742. ; waiting.
  743. ServiceRecv    
  744.  
  745. ; *** Place code here that retrieves a byte of data from the port ***
  746.  
  747. ; Call MIDI tools this way if no error has occurred on receive (<A>
  748. ; contains the data read)
  749. RecvOK
  750.                jsl InBufGlue        ; call the MIDI tools
  751.                bra ServicePort      ; and check for more data in or out
  752.     
  753. ; Call MIDI this way if a reception error has occurred (<A> contains the
  754. ; data read)
  755. RecvErr    
  756.                ldy #miDevReadErr    ; load Y with the error
  757.                jsl InErrGlue        ; call the midi tools
  758.                bra ServicePort
  759.  
  760. ; The routine ServiceXmit will be called when the port is ready to send data.
  761. ; it will actually call the MIDI tools and get a character to send.
  762. ServiceXmit
  763.  
  764.                jsl OutBufGlue       ; call the MIDI tools for the next char
  765.                bcs NoMoreData       ; if the carry set then no data to send
  766.     
  767. ; *** at this point the byte to transmit is in <A>, place your code to output
  768. ; it thru the port here ***
  769.  
  770.     
  771. ; Now that the data has been sent, you can either loop thru ServicePort again,
  772. ; or you could simply end and wait for the next interrupt to send another
  773. ; character. This sample will simply exit at this point
  774.                bra Done             ; after sending the character end.
  775.  
  776. ; NoMoreData is called when the MIDI Tools said that they did not have any
  777. ; more data to transmit, so we should turn off transmitter interrupts at
  778. ; this point in case our device likes to keep interrupting if its empty.
  779. NoMoreData
  780.                phd                  ; push the direct page reg on the stack
  781.                jsl DRVRXmitIntOff   ; enable xmit interrupts
  782. Done
  783.                pld                  ; restore the DPage
  784.                clc                  ; signal the interrupt as handled
  785.                rtl                  ; and get outta here!
  786.     
  787. ; The routine inbufglue should be called when you received a character from
  788. ; your port with no error and you want to pass it to the MIDI tools.
  789. InBufGlue      pea $0400            ; push on the long address of the
  790.                phd                  ; direct page and a proc status byte
  791.                RTL                  ; and jump back to the MIDI tools
  792.     
  793. ; The routine inErrGlue should be called when you received a character from 
  794. ; your port and an error has occurred. In this case, it should still be passed
  795. ; to the MIDI driver, as it may still be useful
  796. inErrGlue      pea $0500            ; push on the long address of the
  797.                phd                  ; direct page and a proc status byte
  798.                RTL                  ; and jump back to the MIDI tools
  799.     
  800. ; The routine OutBufGlue should be called when you are ready to send a char
  801. ; out your port. The MIDI tools will will return with the character to send
  802. ; in <A>. If the MIDI tools have no more characters to send then OutBufGlue
  803. ; will return with the carry set.
  804. OutBufGlue     pea $8400            ; push on the long address of the
  805.                phd                  ; direct page and a proc status byte
  806.                RTL                  ; and jump back to the MIDI tools
  807.                EndP
  808.     
  809.                EJECT
  810. ******************************************************************************
  811. *
  812. *
  813. DRVRPollRecv    PROC
  814. *
  815. * Description:    This routine is called by the MIDI tools when it wants to
  816. *                 pool the port for data instead of waiting for an interrupt.
  817. *                 its function is similar to that of the our interrupt handler
  818. *                 except that it only does input.
  819. *
  820. * Inputs:     None
  821. *
  822. * Outputs:    Carry set if interrupt not serviced
  823. *
  824. * External Refs:    
  825.         Import InBufGlue
  826.         Import InErrGlue
  827. *
  828. * Entry Points:    None
  829. *
  830. ******************************************************************************
  831. *
  832.  
  833.                phd                  ; first, save the current dpage
  834.                tcd                  ; and use the MIDI DPage
  835.                php
  836.                SEI
  837.  
  838. ServicePort                         ; the interrupt was ours, continue
  839.  
  840. ; This routine should test the port too see if the port has any data for use
  841. ; to receive. If it does, it calls the MIDI tools and hands it off. Also note
  842. ; this routine will turn off interrupts, since we wouldn't want any stray
  843. ; receiver interrupts to spoil our fun and grab the data from us. (This is
  844. ; very important for certain types of ports which may signal that the port
  845. ; is ready and the generate an interrupt, thus leaving us in a situation where
  846. ; our interrupt routines could steal the interrupt right out from under us 
  847. ; before we fetched it, thus allowing us to possibly double post a character.
  848.  
  849. ; *** Insert code here to test for received data ***
  850.  
  851.                bne ServiceRecv      ; if chars waiting try receive it 
  852.  
  853. ; If no more data is waiting  exit this routine.
  854.                plp
  855.                pld                  ; restore the direct page
  856.                clc                  ; clear the carry no errors possible
  857.                RTL                  ; and return
  858.     
  859. ; The following routine ServiceRecv will be called when a character is waiting
  860. ; It should retrieve that character, pass it to the MIDI drivers, and then
  861. ; branch back to the beginning of ServicePort, to see if any more chars are
  862. ; waiting.
  863. ServiceRecv    
  864.  
  865. ; *** Place code here that retrieves a byte of data from the port ***
  866.  
  867. ; Call MIDI tools this way if no error has occurred on receive (<A> contains 
  868. ; the data read)
  869. RecvOK
  870.                jsl InBufGlue        ; call the MIDI tools
  871.                bra ServicePort      ; and check for more data in or out
  872.     
  873. ; Call MIDI this way if a reception error has occurred (<A> contains the
  874. ; data read)
  875. RecvErr    
  876.                ldy #miDevReadErr    ; load Y with the error
  877.                jsl InErrGlue        ; call the midi tools
  878.                bra ServicePort    
  879.                EndP
  880.                EJECT
  881.  
  882. ******************************************************************************
  883. *
  884. *
  885. DRVRPollXMit    PROC
  886. *
  887. * Description:    This routine is called when the MIDI tools wants to start 
  888. *                 an output stream. The tool set calls this routine for the 
  889. *                 first character of data, and then this routine is 
  890. *                 responsible for enabling transmitter interrupts and sending
  891. *                 the character.
  892. *
  893. *
  894. * Inputs:     None
  895. *
  896. * Outputs:    Carry set if interrupt not serviced
  897. *
  898. * External Refs:    None
  899.         Import OutBufGlue
  900.         Import DRVRXmitIntOn
  901. *
  902. * Entry Points:    None
  903. *
  904. ******************************************************************************
  905. *
  906.     
  907.                phd                  ; first, save the current dpage
  908.                tcd                  ; and use the MIDI DPage
  909.                php                  ; disable interrupts as we are now going
  910.                SEI                  ; to turn on xmitter interrupts.
  911.  
  912. ; First see if the port is ready to send any data, if not simply exit
  913.  
  914. ; *** Insert code here to test if output is ready ***
  915.  
  916.                bcs Done             ; if not, then simply end
  917.         
  918. ; The port is ready to accept a character for output so, call MIDI tools
  919. ; to get the next character
  920.  
  921.                jsl OutBufGlue       ; get the next character
  922.                bcs Done             ; if carry set, no chars to xmit so end
  923.     
  924.     
  925.                pha                  ; save the character to send
  926.                phd                  ; push the direct page reg on the stack
  927.                jsl DRVRXmitIntOn    ; enable xmit interrupts
  928.                pla                  ; retrieve the character to send
  929.     
  930. ; *** Insert code here to transmit a character ***
  931. Done
  932.                plp                  ; get the old interrupt status
  933.                pld                  ; get the old direct page
  934.                lda #0               ; no errors are possible
  935.                clc
  936.                rtl
  937.  
  938.                EndP    
  939.  
  940.                EJECT
  941.  
  942. ******************************************************************************
  943. *
  944. *
  945. DRVRXmitIntOn    PROC
  946. *
  947. * Description:    This routine will be called when the MIDI tools need to
  948. *                 enable transmitter interrupts on your device.
  949. *
  950. *
  951. * Inputs:     None
  952. *
  953. * Outputs:    None
  954. *
  955. * External Refs:
  956. *
  957. * Entry Points:
  958. *
  959. ******************************************************************************
  960. *
  961.  
  962.                php                  ; save proc status/interrupt state
  963.                phd                  ; save the old direct page
  964.                tcd                  ; use the MIDI tools DPage
  965.                SEI                  ; disable interrupts 
  966.     
  967. ; *** Insert code here to enable transmitter interrupts on your device
  968.  
  969.                pld                  ; recover old direct page
  970.                plp                  ; recover old interrupt state
  971.                lda #0               ; and return no-error (none possible)
  972.                clc
  973.                rtl
  974.                EndP
  975.     
  976. ******************************************************************************
  977. *
  978. *
  979. DRVRXmitIntOff    PROC
  980. *
  981. * Description:    This routine will be called when the MIDI tools need to
  982. *                 Disable transmitter interrupts on your device.
  983. *
  984. *
  985. * Inputs:     None
  986. *
  987. * Outputs:    None
  988. *
  989. * External Refs:
  990. *
  991. * Entry Points:
  992. *
  993. ******************************************************************************
  994. *
  995.  
  996.                php                  ; save proc status/interrupt state
  997.                phd                  ; save the old direct page
  998.                tcd                  ; use the MIDI tools DPage
  999.                SEI                  ; disable interrupts 
  1000.     
  1001. ; *** Insert code here to Disable transmitter interrupts on your device
  1002.  
  1003.                pld                  ; recover old direct page
  1004.                plp                  ; recover old interrupt state
  1005.                lda #0               ; and return no-error (none possible)
  1006.                clc
  1007.                rtl
  1008.                EndP
  1009.  
  1010.                EJECT
  1011.  
  1012. ******************************************************************************
  1013. *
  1014. *
  1015. DRVRRecvIntOn    PROC
  1016. *
  1017. * Description:    This routine will be called when the MIDI tools need to
  1018. *                 enable receiver interrupts on your device.
  1019. *
  1020. *
  1021. * Inputs:     None
  1022. *
  1023. * Outputs:    None
  1024. *
  1025. * External Refs:
  1026. *
  1027. * Entry Points:
  1028. *
  1029. ******************************************************************************
  1030. *
  1031.  
  1032.                php                  ; save proc status/interrupt state
  1033.                phd                  ; save the old direct page
  1034.                tcd                  ; use the MIDI tools DPage
  1035.                SEI                  ; disable interrupts 
  1036.     
  1037. ; *** Insert code here to enable receiver interrupts on your device
  1038.  
  1039.                pld                  ; recover old direct page
  1040.                plp                  ; recover old interrupt state
  1041.                lda #0               ; and return no-error (none possible)
  1042.                clc
  1043.                rtl
  1044.                EndP
  1045.  
  1046.  
  1047. ******************************************************************************
  1048. *
  1049. *
  1050. DRVRRecvIntOff    PROC
  1051. *
  1052. * Description:    This routine will be called when the MIDI tools need to
  1053. *                 Disable receiver interrupts on your device.
  1054. *
  1055. *
  1056. * Inputs:     None
  1057. *
  1058. * Outputs:    None
  1059. *
  1060. * External Refs:
  1061. *
  1062. * Entry Points:
  1063. *
  1064. ******************************************************************************
  1065. *
  1066.  
  1067.                php                  ; save proc status/interrupt state
  1068.                phd                  ; save the old direct page
  1069.                tcd                  ; use the MIDI tools DPage
  1070.                SEI                  ; disable interrupts 
  1071.     
  1072. ; *** Insert code here to Disable receiver interrupts on your device
  1073.  
  1074.                pld                  ; recover old direct page
  1075.                plp                  ; recover old interrupt state
  1076.                lda #0               ; and return no-error (none possible)
  1077.                clc
  1078.                rtl
  1079.                EndP
  1080.     
  1081.     
  1082. ******************************************************************************
  1083. *
  1084. *
  1085. DRVRNotImplemented    PROC
  1086. *
  1087. * Description:    Dummy routine, should leave the stack alone and return
  1088. *                 no error 
  1089. *
  1090. *
  1091. * Inputs:     None
  1092. *
  1093. * Outputs:    None
  1094. *
  1095. * External Refs:
  1096. *
  1097. * Entry Points:
  1098. *
  1099. ******************************************************************************
  1100. *
  1101.                lda #0
  1102.                clc
  1103.                RTL
  1104.                EndP
  1105.     
  1106.                END
  1107.  
  1108.  
  1109. Further Reference:
  1110. _____________________________________________________________________________
  1111.     o    Apple IIGS Toolbox Reference Update